﻿' ----------------------------------------------------------------------
'
' Filename.vb
'
'   Dbfs.Domain
'
'   This class contains the implementation of a s...
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    10/21/09  12:01:00
' ident:    "@(#)/Dbfs/Domain/Filename.vb 1.0 0.1 10/21/09 0 rjs"

Imports System.IO


Namespace Dbfs
    ''' <summary>
    ''' The Identifier data type is a text string. Identifiers may contain the ASCII 
    ''' characters A-Z (a-z), digits, underscores (_), or periods (.). 
    ''' However, every identifier must begin with either a letter or an underscore.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Filename
        Implements IComparable

        Friend _value As String
        Friend _shortName As String


#Region " Constructors "
        Public Sub New()
            _value = Nothing
        End Sub

        ''' <summary>
        ''' Initializes a new <see cref="Filename"/> object.
        ''' </summary>
        ''' <param name="value">A String representing a filename.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal value As String)
            SetName(value)
        End Sub

        Public Sub New(ByVal name As String, ByVal shortName As String)
            If shortName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 Then
                Throw New ArgumentException("Filename contains invalid characters.")
            End If
            SetName(name)
            _shortName = shortName
        End Sub
#End Region ' Constructors

#Region " Properties "
        Public ReadOnly Property Extension() As String
            Get
                Dim i As Int16 = _value.LastIndexOf("."c)
                If i > 0 Then
                    Return _value.Substring(i + 1)
                End If
                Return String.Empty
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the 8.3 short name for the object's <see cref="Value"/>.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ShortName() As String
            Get
                Return _shortName
            End Get
            Set(ByVal value As String)
                If value IsNot Nothing AndAlso _
                   value.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0 Then
                    Throw New ArgumentException("Filename contains invalid characters.")
                End If
                _shortName = value
            End Set
        End Property

        ''' <summary>
        ''' Returns or sets the value for the <see cref="Filename"/>.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Value() As String
            Get
                Return _value
            End Get
            Set(ByVal value As String)
                SetName(value)
            End Set
        End Property
#End Region ' Properties

#Region " Instance Methods "
        ''' <summary>
        ''' Returns the filename.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Return _value
        End Function
#End Region ' Instance Methods

#Region " Shared Methods "
        ''' <summary>
        ''' Gets the filename portion of the passed <paramref name="path"/> without
        ''' any extension.
        ''' </summary>
        ''' <param name="path"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend Shared Function Basename(ByVal path As String) As String

            Dim i As Integer

            ' remove any leading directories
            i = InStrRev(path, "\")
            If i > 0 Then path = path.Substring(i + 1)

            ' remove any "." extension
            i = InStrRev(path, ".")
            If i > 0 Then path = path.Substring(0, i - 1)

            ' return the basename
            Return path

        End Function

        Friend Shared Function ShortNameOf(ByVal name As String) As String

            Dim ext As String = ""
            Dim k As Int16
            Dim littleName As String

            ' trim any excess space
            name = name.Trim
            k = name.LastIndexOf("."c)

            If k = 0 Then
                name = name.Substring(1)
            ElseIf k > 0 Then
                ' ext will contain the "."
                ext = name.Substring(k).ToUpper
                name = name.Substring(0, k)
            End If

            ' if the name does not exceed the limits of a short-name,
            ' then there is no short name
            If name.Length <= 8 And ext.Length <= 4 Then
                Return Nothing
            End If

            ext = Left(ext, 4)
            name = name.Replace(" ", String.Empty)
            name = Left(name, 8)
            littleName = Left(name, 6)

            Return littleName.ToUpper & "~1" & ext.ToUpper

        End Function

        ''' <summary>
        ''' Gets the 8.3 short name for <paramref name="name"/> such that it does not duplicate
        ''' any name found in <paramref name="nameList"/>.
        ''' </summary>
        ''' <param name="name">The long filename.</param>
        ''' <param name="nameList">A List of String containing the short names that are
        ''' already in use.</param>
        ''' <returns>The 8.3 short name for <paramref name="name"/>.</returns>
        ''' <remarks></remarks>
        Public Shared Function ShortNameOf(ByVal name As String, ByVal nameList As List(Of String))

            Dim ext As String = ""
            Dim k As Int16
            Dim littleName As String

            ' trim any excess space
            name = name.Trim
            k = name.LastIndexOf("."c)

            If k = 0 Then
                name = name.Substring(1)
            ElseIf k > 0 Then
                ' ext will contain the "."
                ext = name.Substring(k).ToUpper
                name = name.Substring(0, k)
            End If

            ' if the name does not exceed the limits of a short-name,
            ' then there is no short name
            If name.Length <= 8 And ext.Length <= 4 Then
                Return String.Empty
            End If

            ext = Left(ext, 4).ToUpper
            name = name.Replace(" ", String.Empty)
            name = Left(name, 8)
            name = name.ToUpper()

            littleName = Left(name, 6)
            For i = 1 To 9
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            littleName = Left(name, 5)
            For i = 10 To 99
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            littleName = Left(name, 4)
            For i = 100 To 999
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            littleName = Left(name, 3)
            For i = 1000 To 9999
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            littleName = Left(name, 2)
            For i = 10000 To 99999
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            littleName = Left(name, 1)
            For i = 100000 To 999999
                Dim tryName As String = littleName & "~" & i & ext
                If Not nameList.Contains(tryName) Then
                    Return tryName
                End If
            Next

            Return String.Empty

        End Function
#End Region ' Shared Methods

#Region " Private Methods "
        ''' <summary>
        ''' Checks that the passed value is a valid filename, sets the name to that value
        ''' and gets an appropriate short name for the value.
        ''' </summary>
        ''' <param name="value"></param>
        ''' <remarks></remarks>
        Private Sub SetName(ByVal value As String)
            If value.IndexOfAny(Path.GetInvalidPathChars()) >= 0 Then
                Throw New ArgumentException("Filename contains invalid characters.")
            End If
            _value = value
            _shortName = ShortNameOf(value)
        End Sub
#End Region ' Private Methods

#Region " Interface Methods "
        ''' <summary>
        ''' Compares the current instance with another object of the same type and returns an integer 
        ''' that indicates whether the current instance precedes, follows, or occurs in the same 
        ''' position in the sort order as the other object.
        ''' </summary>
        ''' <param name="obj">An object to compare with this instance.</param>
        ''' <returns>A 32-bit signed integer that indicates the relative order of the objects being compared.</returns>
        ''' <remarks></remarks>
        Public Overloads Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
            If TypeOf obj Is Filename Then
                Dim otherFilename As Filename = DirectCast(obj, Filename)
                Return Me._value.CompareTo(otherFilename._value)
            Else
                Throw New ArgumentException("Object is not a Filename.")
            End If
        End Function
#End Region ' Interface Methods

    End Class

End Namespace